package partitions;

import hips.region.NewRegionEvent;
import hips.region.NewRegionListener;
import hips.Partition;
import hips.gui.ComponentLocation;
import hips.gui.images.Image3;
import hips.gui.images.Image4;
import hips.images.ImagePartitionable;
import hips.pixel.PixelValue;
import hips.region.Region;
import ij.gui.StackWindow;
import java.awt.Color;
import java.awt.Container;
import java.awt.Scrollbar;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Date;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Listener implements WindowListener, MouseListener, MouseMotionListener, MouseWheelListener, NewRegionListener, AdjustmentListener, ChangeListener {

    private static long ELAPSED = 50;
    private long time;
    private JTabbedPane tabs;
    private Partition p;
    private InfoPanel info;
    private hips.images.Image output;
    private ImagePartitionable f;
    private boolean highlightedEnabled;
    private Region highlighted;
    private int lastPixel;

    public Listener(ImagePartitionable img, JTabbedPane tabs, PixelValue alpha, PixelValue omega, float cindex) {
        f = img;
        this.tabs = tabs;
        output = hips.gui.images.Image.getOutputImage(img);
        output.getImagePlus().show();
        lastPixel = 0;

        tabs.addChangeListener(this);

        ComponentLocation.setLocationCenter(0.75f, 0.5f, output.getImagePlus().getWindow());

        p = f.newPartition(alpha, omega, cindex);
        p.addNewRegionEventListener(this);

        info = new InfoPanel();
        tabs.add(info);
        tabs.setTitleAt(tabs.getTabCount() - 1, "partition " + tabs.getTabCount());
        tabs.setSelectedIndex(tabs.getTabCount() - 1);
        showLocalAndGlobal();
        info.cindex.setText("" + cindex);
        info.pixels.setText("" + f.getSize());

        time = System.currentTimeMillis();
        p.makeRegions();
        time = System.currentTimeMillis() - time;

        info.components.setText("" + p.getRegionSize());
        info.elapsed.setText("" + time);

        if (output.getImagePlus().getStackSize() > 1) {
            StackWindow stack = (StackWindow) output.getImagePlus().getWindow();
            Container c = (Container) stack.getComponent(1);
            Scrollbar s = (Scrollbar) c.getComponent(1);
            s.addAdjustmentListener(this);
        }

        output.getImagePlus().getCanvas().addMouseListener(this);
        output.getImagePlus().getCanvas().addMouseMotionListener(this);
        output.getImagePlus().getWindow().addWindowListener(this);
        output.getImagePlus().getWindow().addMouseWheelListener(this);

        highlightedEnabled = true;
        highlighted = null;
        time = System.currentTimeMillis();
    }

    public void close() {
        if (output.getImagePlus() != null) {
            output.getImagePlus().getCanvas().removeMouseListener(this);
            output.getImagePlus().getCanvas().removeMouseMotionListener(this);
            output.getImagePlus().getWindow().removeWindowListener(this);
            output.getImagePlus().getWindow().removeMouseWheelListener(this);
            output.getImagePlus().close();
        }
    }

    public void adjustmentValueChanged(AdjustmentEvent e) {
        showLocalAndGlobal();
        showColor(lastPixel);
    }

    private void showLocalAndGlobal() {
        int slice = output.getImagePlus().getSlice() - 1;
        if (output instanceof Image3) {
            info.localRange.setText(Image3.getStringFrom(p.getAlpha()));
            info.globalRange.setText(Image3.getStringFrom(p.getOmega()));
        } else if (output instanceof Image4) {
            info.localRange.setText(Image4.getStringFrom(p.getAlpha(), slice));
            info.globalRange.setText(Image4.getStringFrom(p.getOmega(), slice));
        } else {
            info.localRange.setText(p.getAlpha().getString(slice));
            info.globalRange.setText(p.getOmega().getString(slice));
        }
    }

    private void showColor(int px) {
        PixelValue pixel = f.getPixelValue(px);
        Region r = p.getRegionByPixel(px);
        int slice = output.getImagePlus().getSlice() - 1;
        if (output instanceof Image3) {
            info.color.setText(Image3.getStringFrom(pixel));
            info.colorRegion.setText(Image3.getStringFrom(f.getMeanValue(r)));
        } else if (output instanceof Image4) {
            info.color.setText(Image4.getStringFrom(pixel, slice));
            info.colorRegion.setText(Image4.getStringFrom(f.getMeanValue(r), slice));
        } else {
            info.color.setText(pixel.getString(slice));
            info.colorRegion.setText(f.getMeanValue(r).getString(slice));
        }
    }

    public void mouseClicked(MouseEvent e) {
        if (e.getButton() != MouseEvent.BUTTON1) {
            return;
        }
        int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
        int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
        int pos = output.getWidth() * y + x;
        Region r = p.getRegionByPixel(pos);

        highlightedEnabled = !highlightedEnabled;

        if (highlightedEnabled) {
            highlighted = r;
            output.paintRegion(highlighted, output.newPixelValue(Color.YELLOW.getRGB()));
        } else {
            if (highlighted != null) {
                output.paintRegion(highlighted, f.toRGB(f.getMeanValue(highlighted)));
            }
        }
    }

    public void mouseMoved(MouseEvent e) {
        int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
        int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
        int pos = output.getWidth() * y + x;
        Region r = p.getRegionByPixel(pos);
        int slice = output.getImagePlus().getSlice() - 1;
        if (highlighted == null && highlightedEnabled) {
            highlighted = r;
            output.paintRegion(r, output.newPixelValue(Color.YELLOW.getRGB()));
        } else if (highlighted != null && !r.sameAs(highlighted) && highlightedEnabled) {
            output.paintRegion(highlighted, f.toRGB(f.getMeanValue(highlighted)));
            output.paintRegion(r, output.newPixelValue(Color.YELLOW.getRGB()));
            highlighted = r;
        }
        if ((new Date()).getTime() - time > ELAPSED) {
            lastPixel = pos;
            info.pixel.setText(xy(x, y));
            showColor(lastPixel);
            info.region.setText("" + p.getRegionByPixel(pos).getLabel());
            info.size.setText("" + r.getSize());
            time = System.currentTimeMillis();
        }
    }

    public void windowClosed(WindowEvent e) {
        for (int i = 0; i < tabs.getTabCount(); i++) {
            if (tabs.getComponent(i) == info) {
                tabs.remove(i);
                break;
            }
        }
    }

    public void windowDeactivated(WindowEvent e) {
        if (highlighted != null) {
            output.paintRegion(highlighted, f.toRGB(f.getMeanValue(highlighted)));
            highlighted = null;
        }
    }

    public void windowActivated(WindowEvent e) {
        for (int i = 0; i < tabs.getTabCount(); i++) {
            if (tabs.getComponent(i) == info) {
                tabs.setSelectedIndex(i);
                break;
            }
        }
    }

    public static String xy(int x, int y) {
        return "(" + x + ", " + y + ")";
    }

    public void newRegionCreated(NewRegionEvent e) {
        Region r = e.getRegion();
        output.paintRegion(r, f.toRGB(f.getMeanValue(r)));
    }

    public void stateChanged(ChangeEvent e) {
        if (tabs.getSelectedComponent() == info) {
            output.getImagePlus().getWindow().toFront();
        }
    }

    public void mouseWheelMoved(MouseWheelEvent e) {
        showLocalAndGlobal();
        showColor(lastPixel);
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseDragged(MouseEvent e) {
    }

    public void windowOpened(WindowEvent e) {
    }

    public void windowClosing(WindowEvent e) {
    }

    public void windowIconified(WindowEvent e) {
    }

    public void windowDeiconified(WindowEvent e) {
    }
}
